/**************************************************************************************************
* Filename:			CTimer.cpp
* Date:				05/09/10
* Mod. Date:		05/16/10
* Mod. Initials:	MG
* Author:			Malcolm J. Gruber
* Purpose:			A Timer system for getting time, and alarm and stop watch functionality.
**************************************************************************************************/

#include "Timer.h"

CTimer	CTimer::m_siInstance;

/*************************************************************************
* GetInstance():				Returns an instance to the timer class.
*
* Ins:							void
*
* Outs:							void
*
* Returns:						CTimer*
*
* Mod. Date:					05/16/10
* Mod. Initials:				MG
*************************************************************************/
CTimer* CTimer::GetInstance(void)
{
	return &m_siInstance;
}

/********************************************************************************
*	CAlarm
********************************************************************************/
CTimer::CAlarm::CAlarm(CTimer* pOwner) : 
	m_pOwner(pOwner), m_fTime(0), m_dwCurrentTime(0), m_fpToExecute(NULL), m_szRunning(0)
{
}

CTimer::CAlarm::~CAlarm(void)
{
	// Null the pointers
	m_pOwner = NULL;
	m_fpToExecute = NULL;
}

void CTimer::CAlarm::Start(float amount, void(*functionToExecute)(void))
{
	// Set the time and get current time
	m_fTime = amount;
	m_dwCurrentTime = m_pOwner->m_dwCurrentTime;
	// Set function pointer
	m_fpToExecute = functionToExecute;
	m_szRunning = 1;
}

void CTimer::CAlarm::Update(void)
{
	if(	m_szRunning )
	{
		// Get the change in time
		float m_fDTime = (float)(m_pOwner->m_dwCurrentTime-m_dwCurrentTime)/1000.0f;
		// Get the new current time
		m_dwCurrentTime = m_pOwner->m_dwCurrentTime;
		// Subtract the change in time for the amount of time left
		m_fTime -= m_fDTime;
		
		// If the time left is less than or equal to 0, execute the function if there is one
		if( m_fTime <= 0 ) {
			m_fTime = -1;
			if( m_fpToExecute ) {
				m_fpToExecute();
			}
			m_szRunning = 0;
		}
	}
}

void CTimer::CAlarm::Stop(void)
{
	// Stop the current alarm
	m_fpToExecute = NULL;
	m_dwCurrentTime = 0;
	m_fTime = 0;
	m_szRunning = 0;
}

float CTimer::CAlarm::Check(void)
{
	// Return the amount of time left
	return m_fTime;
}

/********************************************************************************
*	CStopWatch
********************************************************************************/
CTimer::CStopWatch::CStopWatch(CTimer* pOwner) : 
	m_pOwner(pOwner), m_fTime(0), m_szRunning(0), m_fFrequency(0), m_dwStartTime(0), m_dwCurrentTime(0), m_fpToExecute(NULL)
{
}

CTimer::CStopWatch::~CStopWatch(void)
{
	// Null the pointers
	m_pOwner = NULL;
	m_fpToExecute = NULL;
}

void CTimer::CStopWatch::Start(float frequency, void(*functionToExecute)(void))
{
	// Set the frequency, than time passed to 0
	m_fFrequency = frequency;
	m_fTime = 0.0f;
	// Set the function to execute every time time hits or goes past the frequency
	m_fpToExecute = functionToExecute;
	// Set the current and start time
	m_dwStartTime = m_dwCurrentTime = m_pOwner->m_dwCurrentTime;
	// Set that it's running
	m_szRunning = 1;
}

void CTimer::CStopWatch::Continue(void)
{
	// Move the time up as too not mess up any calculations
	m_dwStartTime += m_pOwner->m_dwCurrentTime - m_dwCurrentTime; 
	m_dwCurrentTime = m_pOwner->m_dwCurrentTime;
	m_szRunning = 1;
}

void CTimer::CStopWatch::Update(void)
{
	if( m_szRunning )
	{
		// Get the change in time and than set the new current time
		float m_fDTime = (float)(m_pOwner->m_dwCurrentTime-m_dwCurrentTime)/1000.0f;
		m_dwCurrentTime = m_pOwner->m_dwCurrentTime;
		
		// Add the change in time
		m_fTime += m_fDTime;

		// Check to make sure there is no NULL frequency
		if( m_fFrequency <= 0.0f )
			return;
		
		// Reset the amount of time passed and execute the function
		if( m_fTime >= m_fFrequency ) {
			m_fTime -= m_fFrequency;
			if( m_fpToExecute ) {
				m_fpToExecute();
			}
		}
		
	}
}

float CTimer::CStopWatch::Stop(void)
{
	// Stop the current stop watch
	float m_fTotalTime = (float)(m_dwCurrentTime-m_dwStartTime)/1000.0f;
	m_szRunning = 0;
	return m_fTotalTime;
}

float CTimer::CStopWatch::GetTimeCount(void)
{
	// Return the amount of time that the stop watch has been running for
	// return (float)(m_pOwner->m_dwCurrentTime-m_dwStartTime)/1000.0f;
	return m_fTime;
}

bool CTimer::CStopWatch::IsRunning(void)
{
	// Return if the stop watch is running
	return m_szRunning ? true : false;
}

/********************************************************************************
*	CTimer
********************************************************************************/
CTimer::CTimer(void)
{
	Reset();
}

CTimer::~CTimer(void)
{
	ClearAlarms();
	ClearWatches();
}

void CTimer::InitalizeTimer(void)
{
	Reset();
}

void CTimer::Reset(void)
{
	m_dwStartTime = GetTickCount();
	m_dwPrevTime = m_dwCurrentTime = m_dwStartTime;
	m_fDTime = 0.0f;
	m_fTotTime = 0.0f;
}

void CTimer::Update(void)
{
	m_dwCurrentTime = GetTickCount();
	m_fDTime = (float)(m_dwCurrentTime-m_dwPrevTime)/1000.0f;
	m_fTotTime += m_fDTime;
	m_dwPrevTime = m_dwCurrentTime;
	UpdateAlarms();
	UpdateWatches();
}

void CTimer::ShutdownTimer(void)
{
	Reset();
	ClearAlarms();
	ClearWatches();
}

float CTimer::GetElapsedTime(void)
{
	return m_fDTime;
}

float CTimer::GetTotalTime(void)
{
	return m_fTotTime;
}

unsigned int CTimer::CreateAlarm(void)
{
	for(unsigned int i = 0; i < m_cAlarms.Size(); i++)
	{
		if( m_cAlarms[i] == NULL )
		{
			m_cAlarms[i] = new CAlarm(this);
			return i;
		}
	}

	CAlarm* newAlarm = new CAlarm(this);
	m_cAlarms.AddBack(newAlarm);
	return m_cAlarms.Size()-1;
}

void CTimer::StartAlarm(unsigned int id, float amount, void(*functionToExecute)(void))
{
	if( id >= m_cAlarms.Size() )
		return;

	if( m_cAlarms[id] )
	{
		m_cAlarms[id]->Start(amount, functionToExecute);
	}
}

void CTimer::UpdateAlarms(void)
{
	for(unsigned int i = 0; i < m_cAlarms.Size(); i++)
	{
		if( m_cAlarms[i] )
		{
			m_cAlarms[i]->Update();
		}
	}
}

void CTimer::RemoveAlarm(unsigned int id)
{
	if( id >= m_cAlarms.Size() )
		return;

	if( !m_cAlarms[id] )
		return;

	m_cAlarms[id]->Stop();
	CAlarm* temp = m_cAlarms[id];
	m_cAlarms[id] = NULL;
	delete temp;
}

float CTimer::CheckAlarm(unsigned int id)
{
	if( id >= m_cAlarms.Size() )
		return -1.0f;

	if( m_cAlarms[id] )
	{
		return m_cAlarms[id]->Check();
	}

	return -1.0f;
}

void CTimer::ClearAlarms(void)
{
	CAlarm* temp = NULL;
	for(unsigned int i = 0; i < m_cAlarms.Size(); i++)
	{
		if( m_cAlarms[i] )
		{
			temp = m_cAlarms[i];
			m_cAlarms[i] = NULL;
			delete temp;
		}
	}
	m_cAlarms.RemoveAll();
}

unsigned int CTimer::CreateWatch(void)
{
	for(unsigned int i = 0; i < m_cStopWatches.Size(); i++)
	{
		if( m_cStopWatches[i] == NULL )
		{
			m_cStopWatches[i] = new CStopWatch(this);
			return i;
		}
	}

	CStopWatch* newWatch = new CStopWatch(this);
	m_cStopWatches.AddBack(newWatch);
	return m_cStopWatches.Size()-1;
}

void CTimer::StartWatch(unsigned int id, float frequency, void(*functionToExecute)(void))
{
	if( id >= m_cStopWatches.Size() )
		return;

	if( m_cStopWatches[id] )
	{
		m_cStopWatches[id]->Start(frequency, functionToExecute);
	}
}

void CTimer::UpdateWatches(void)
{
	for(unsigned int i = 0; i < m_cStopWatches.Size(); i++)
	{
		if( m_cStopWatches[i])
		{
			m_cStopWatches[i]->Update();
		}
	}
}

void CTimer::ClearWatches(void)
{
	CStopWatch* temp = NULL;
	for(unsigned int i = 0; i < m_cStopWatches.Size(); i++)
	{
		if( m_cStopWatches[i] )
		{
			temp = m_cStopWatches[i];
			m_cStopWatches[i] = NULL;
			delete temp;
		}
	}
	m_cStopWatches.RemoveAll();
}

void CTimer::ContinueWatch(unsigned int id)
{
	if( id >= m_cStopWatches.Size() )
		return;

	if( m_cStopWatches[id] )
	{
		m_cStopWatches[id]->Continue();
	}
}

float CTimer::StopWatch(unsigned int id)
{
	if( id >= m_cStopWatches.Size() )
		return -1.0f;

	if( m_cStopWatches[id] )
	{
		return m_cStopWatches[id]->Stop();
	}

	return -1.0f;
}

void CTimer::RemoveWatch(unsigned int id)
{
	if( id >= m_cStopWatches.Size() )
		return;

	if( !m_cStopWatches[id] )
		return;

	m_cStopWatches[id]->Stop();
	CStopWatch* temp = m_cStopWatches[id];
	m_cStopWatches[id] = NULL;
	delete temp;
}

float CTimer::GetWatchTime(unsigned int id)
{
	if( id >= m_cStopWatches.Size() )
		return -1.0f;

	return m_cStopWatches[id] ? m_cStopWatches[id]->GetTimeCount() : -1.0f;
}

bool CTimer::IsWatchRunning(unsigned int id)
{
	if( id >= m_cStopWatches.Size() )
		return false;

	if( m_cStopWatches[id] )
	{
		return m_cStopWatches[id]->IsRunning();
	}

	return NULL;
}